package domein;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Class to receive files from another client.
 *
 * @author Colin Bundervoet
 * @author Jeroen Pattyn
 */
public class FileReceiver
{
    private String ip;
    private int port;
    private Socket socket;
    private InputStream input;
    private String fileName;
    private String fileLocation;
    private int fileSize;
    private ReceivingFileObservable receivingFile;

    /**
     * Sets all necessary data to receive the file properly,
     *
     * @param ip of the sending user
     * @param port port being used to send files
     * @param fileName the filename being transfered
     * @param fileSize the size of the file being received
     * @param receivingFile Observable to inform gui of updates.
     */
    public FileReceiver(String ip, int port, String fileName, int fileSize, ReceivingFileObservable receivingFile) {
        this.ip = ip;
        this.port = port;
        this.fileName = fileName;
        this.fileSize = fileSize;
        this.receivingFile = receivingFile;
    }

    /**
     * Creates the initial connections to receives the file and opens a socket
     * to sender.
     *
     * @param file to be received
     */
    public void connect(File file) {
        try {
            socket = new Socket(ip, port);
            input = socket.getInputStream();
            receive(file);
        }
        catch (UnknownHostException ex) {
            Logger.getLogger(FileReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex) {
            Logger.getLogger(FileReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Initiates actual file transfer. Invoke after opening the socket and
     * connecting with the host. Invoke close after file transfer.
     *
     * @param file file object to be received.
     */
    private void receive(File file) {

        FileOutputStream fos = null;
        try {
            int bytesRead;
            int current = 0;
            byte[] mybytearray = new byte[1024];
            fos = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            int progres = 0;
            while (1024 == (bytesRead = input.read(mybytearray, current, (mybytearray.length)))) {
                bos.write(mybytearray, 0, bytesRead);
                progres += bytesRead;
                receivingFile.update((progres * 100) / fileSize);
            }
            if (bytesRead != -1) {
                bos.write(mybytearray, 0, bytesRead);
            }
            bos.flush();
            bos.close();
            close();
        }
        catch (IOException ex) {
            Logger.getLogger(FileReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally {
            try {
                fos.close();
            }
            catch (IOException ex) {
                Logger.getLogger(FileReceiver.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Closes all related sockets and streams. Invoke after file transfer is
     * complete.
     */
    public void close() {
        try {
            receivingFile.update(-1);
            input.close();
            socket.close();
        }
        catch (IOException ex) {
            Logger.getLogger(FileReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
